/*------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Copyright (c) 2016, Loongson Technology Corporation Limited.

All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this 
list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, 
this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

3. Neither the name of Loongson Technology Corporation Limited nor the names of 
its contributors may be used to endorse or promote products derived from this 
software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
DISCLAIMED. IN NO EVENT SHALL LOONGSON TECHNOLOGY CORPORATION LIMITED BE LIABLE
TO ANY PARTY FOR DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
------------------------------------------------------------------------------*/

module cpu_axi_interface
(
    input logic         clk,
    input logic        resetn, 

	input logic 		  	ird_req,
	input logic [31: 0]		ird_addr,
	input logic [7: 0]		axi_rd_len,
	output logic  [31: 0]	irdata,
	output logic			iret_valid,
	output logic			iret_last,
	output logic			ird_ready,

	input logic				drd_req,
	input logic [31: 0]		drd_addr,
	output logic [31: 0]	drdata,
	output logic			dret_valid,
	output logic			dret_last,
	output logic			drd_ready,

	input logic				dwr_req,
	input logic 			dwr_last,
	input logic [31: 0]		dwr_addr,
	input logic [31:0]		dwr_wdata,
	output logic			dwr_ready,
	output logic 			dwb_ready,

	input logic 			urd_req,
	input logic [31: 0]		urd_addr,
	input logic [1: 0]		urd_size,
	output logic [31: 0]	urdata,
	output logic 			uret_last,
	output logic 			urd_ok,

	input logic 			uwr_req,
	input logic 			uwr_last,
	input logic [1: 0]		uwr_size,
	input logic [31: 0]		uwr_addr,
	input logic [31: 0]		uwr_wdata,
	input logic [3: 0]		uwr_wstrb,
	output logic 			uwr_end,
	output logic 			uwr_ok,

	input logic 			iu_req,
	input logic [31: 0]		iurd_addr,
	input logic [3: 0]		iurd_len,
	output logic 			iuret_valid,
	output logic 			iuret_last,
	output logic [31: 0]	iuret_data,
	output logic 			iurd_ok,

	output logic 			write_valid,

    //axi
    //ar
    output logic [3 :0] arid         ,
    output logic [31:0] araddr       ,
    output logic [7 :0] arlen        ,
    output logic [2 :0] arsize       ,
    output logic [1 :0] arburst      ,
    output logic [1 :0] arlock        ,
    output logic [3 :0] arcache      ,
    output logic [2 :0] arprot       ,
    output logic        arvalid      ,
    input  logic        arready      ,
    //r           
    input logic [3 :0] rid          ,
    input logic [31:0] rdata        ,
    input logic [1 :0] rresp        ,
    input logic        rlast        ,
    input logic        rvalid       ,
    output logic       rready       ,
    //aw          
    output logic [3 :0] awid         ,
    output logic [31:0] awaddr       ,
    output logic [7 :0] awlen        ,
    output logic [2 :0] awsize       ,
    output logic [1 :0] awburst      ,
    output logic [1 :0] awlock       ,
    output logic [3 :0] awcache      ,
    output logic [2 :0] awprot       ,
    output logic        awvalid      ,
    input logic         awready      ,
    //w          
    output logic [3 :0] wid          ,
    output logic [31:0] wdata        ,
    output logic [3 :0] wstrb        ,
    output logic        wlast        ,
    output logic        wvalid       ,
    input  logic        wready       ,
    //b           
    input logic [3 :0] bid          ,
    input logic [1 :0] bresp        ,
    input logic        bvalid       ,
    output logic       bready       
);
//addr
// logic do_req;
// logic do_req_or; //req is inst or data;1:data,0:inst
// logic        do_wr_r;
// logic [1 :0] do_size_r;
// logic [31:0] do_addr_r;
// logic [31:0] do_wdata_r;
// logic data_back, wdata_back;
// logic inst_addr_ok, data_addr_ok;

// assign inst_addr_ok = !do_req&&!drd_req;
// assign data_addr_ok = !do_req;

typedef enum logic [2: 0] { IDLE, ICACHE, DCACHE, UNCACHE, IUNCACHE } device_t;

typedef struct {
	logic en;
	logic wend;
	device_t device;
	logic [2: 0] len;
	logic [2: 0] size;
	logic [31: 0] addr;
	logic addr_ok;
} WriteController_t;

typedef struct {
	logic en;
	device_t device;
	logic [2: 0] size;
	logic [3: 0] len;
	logic [31: 0] addr;
	logic addr_ok;
} ReadController_t;

WriteController_t write_controller;
ReadController_t read_controller;
logic pre_rd_free;

always_ff @(posedge clk)begin
	if(resetn == 1'b0)begin
		write_controller.en <= 1'b0;
		write_controller.wend <= 1'b0;
		write_controller.device <= IDLE;
		write_controller.addr_ok <= 1'b0;
		write_controller.len <= 0;
		write_controller.addr <= 0;
		write_controller.size <= 0;
		read_controller.en <= 1'b0;
		read_controller.device <= IDLE;
		read_controller.addr_ok <= 1'b0;
		read_controller.len <= 0;
		read_controller.addr <= 0;
		read_controller.size <= 0;
		pre_rd_free <= 0;
	end
	else begin
		if(!write_controller.en && (dwr_req || uwr_req))begin
			write_controller.en <= 1'b1;
			write_controller.addr_ok <= 1'b0;
			write_controller.wend <= 1'b0;
			write_controller.size <= uwr_req ? {1'b0, uwr_size} : 3'b010;
		end
		if(awvalid & awready)begin
			write_controller.addr_ok <= 1'b1;
		end
		if(bready & bvalid)begin
			write_controller.en <= 1'b0;
		end
		if(wvalid & wready & wlast)begin
			write_controller.wend <= 1'b1;
		end
		if(!write_controller.en && dwr_req)begin
			write_controller.device <= DCACHE;
			write_controller.addr <= dwr_addr;
			write_controller.len <= 3'd7;
		end
		else if(!write_controller.en && uwr_req)begin
			write_controller.device <= UNCACHE;
			write_controller.addr <= uwr_addr;
			write_controller.len <= 3'd0;
		end
		pre_rd_free <= !read_controller.en;
		if(!read_controller.en && (ird_req || drd_req || urd_req || iu_req))begin
			read_controller.en <= 1'b1;
			read_controller.addr_ok <= 1'b0;
		end
		if(arvalid & arready)begin
			read_controller.addr_ok <= 1'b1;
		end
		if(rvalid & rready & rlast)begin
			read_controller.en <= 1'b0;
		end
		if(!read_controller.en && (ird_req || drd_req || urd_req || iu_req))begin
			read_controller.device <= iu_req ? IUNCACHE : urd_req ? UNCACHE : drd_req ? DCACHE : ICACHE;
			read_controller.len <= iu_req | urd_req ? 4'd0 : drd_req ? 4'd7 : axi_rd_len[3: 0];
			read_controller.addr <= iu_req ? iurd_addr : urd_req ? urd_addr : drd_req ? drd_addr : ird_addr;
			read_controller.size <= urd_req & ~iu_req ? {1'b0, urd_size} : 3'b010;
		end
	end
end

assign irdata   = rdata;
assign drdata   = rdata;
assign urdata 	= rdata;

assign iret_valid = read_controller.en & (read_controller.device == ICACHE) & rvalid;
assign iret_last = read_controller.en & (read_controller.device == ICACHE) & rlast & rvalid;
assign ird_ready = pre_rd_free & read_controller.en & (read_controller.device == ICACHE);

assign dret_valid = read_controller.en & (read_controller.device == DCACHE) & rvalid;
assign dret_last = read_controller.en & (read_controller.device == DCACHE) & rlast & rvalid;
assign drd_ready = pre_rd_free & read_controller.en & (read_controller.device == DCACHE);

assign dwr_ready = write_controller.en & (write_controller.device == DCACHE) & awvalid & awready;
assign uret_last = read_controller.en & (read_controller.device == UNCACHE) & rlast & rvalid;
assign urd_ok = read_controller.en & arready & arvalid & (read_controller.device == UNCACHE);
assign uwr_ok 	 = write_controller.en & (write_controller.device == UNCACHE) & bready & bvalid;
assign uwr_end = write_controller.en & (write_controller.device == UNCACHE) & wvalid & wready & wlast;

assign iuret_valid = read_controller.en & (read_controller.device == IUNCACHE) & rvalid;
assign iuret_last = read_controller.en & (read_controller.device == IUNCACHE) & rvalid & rlast;
assign iurd_ok = read_controller.en & arready & arvalid & (read_controller.device == IUNCACHE);
assign iuret_data = rdata;

assign write_valid = wvalid & wready;
assign dwb_ready = bready & bvalid;

//ar
assign arid    = 0;
assign araddr  = read_controller.addr;
assign arlen   = {4'b0, read_controller.len};
assign arsize  = read_controller.size;
assign arburst = !(read_controller.device == UNCACHE || read_controller.device == IUNCACHE);
assign arlock  = 2'd0;
assign arcache = 4'd0;
assign arprot  = 3'd0;
assign arvalid = read_controller.en & ~read_controller.addr_ok;
//r
assign rready  = 1'b1;

//aw
assign awid    = 0;
assign awaddr  = write_controller.addr;
assign awlen   = {5'b0, write_controller.len};
assign awsize  = write_controller.size;
assign awburst = write_controller.device == DCACHE;
assign awlock  = 2'd0;
assign awcache = 4'd0;
assign awprot  = 3'd0;
assign awvalid = write_controller.en & ~write_controller.addr_ok;
//w
assign wid    = 0;
assign wdata  = write_controller.device == DCACHE ? dwr_wdata : uwr_wdata;
assign wstrb  = write_controller.device == DCACHE ? 4'b1111 : uwr_wstrb;
assign wvalid = write_controller.en & write_controller.addr_ok & ~write_controller.wend;
assign wlast = write_controller.device == DCACHE ? ~wvalid | wvalid & dwr_last : ~wvalid | wvalid & uwr_last;
//b
assign bready  = 1'b1;

endmodule

